package apiserver

import (
	"bytes"
	"context"
	"fmt"
	"gluenet/internal/pkg/infra"
	"io/ioutil"
	"net/http"
	"net/url"
	"os"
	"strings"
	"time"

	"github.com/casdoor/casdoor-go-sdk/auth"
	"github.com/golang-jwt/jwt/v4"
	"github.com/kataras/iris/v12"
	"gopkg.in/yaml.v3"
)

type Router struct {
	RouterTables map[string]string `json:"router_tables" yaml:"router_tables"`
	RouterUrl    map[url.URL]url.URL
}

func newRouter() *Router {
	return &Router{}
}

func Run(ctx context.Context) {
	file := os.Getenv("CONFIG")
	if file == "" {
		file = "../../internal/apiserver/router.yml"
	}

	bts, err := ioutil.ReadFile(file)
	if err != nil {
		panic(err)
	}

	var table = &Router{}
	if err = yaml.Unmarshal(bts, &table); err != nil {
		panic(err)
	}

	table.RouterUrl = make(map[url.URL]url.URL, len(table.RouterTables))
	for k, v := range table.RouterTables {
		U, err := url.Parse(k)
		if err != nil {
			fmt.Printf("table %v is not an url %v", k, err)
			continue
		}
		P, err := url.Parse(v)
		if err != nil {
			fmt.Printf("table %v is not an url %v", v, err)
			continue
		}
		table.RouterUrl[*U] = *P
	}

	Oauth := OAuth{}
	installDB()

	App := iris.New()
	party := App.Party("/apis", func(ctx iris.Context) {
		// 验证
		token := ctx.GetHeader("token")
		auth, err := Oauth.Auth(token)
		if err != nil {
			ctx.JSON(infra.ResponseReqErr(err, nil))
			return
		}
		id := strings.ReplaceAll(auth.Id, "-", "")
		var authLevel int
		if id == "0" {
			authLevel = 0
		} else if auth.IsAdmin {
			authLevel = 1
		} else {
			authLevel = 2
		}

		ctx.Values().Set("user_id", id)
		ctx.Values().Set("group_id", 100)
		ctx.Values().Set("auth_level ", authLevel)
		ctx.Next()
	})
	App.Get("/login", func(ctx iris.Context) {
		code := ctx.URLParam("code")
		state := ctx.URLParam("state")
		token, err := Oauth.Login(code, state)
		if err != nil {
			ctx.JSON(infra.ResponseReqErr(err, nil))
			return
		}

		ctx.JSON(infra.ResponseOK(token))
	})

	App.Get("/token", func(ctx iris.Context) {
		id := ctx.URLParam("id")
		key, _ := jwt.ParseRSAPrivateKeyFromPEM([]byte(infra.JPrivateKey))
		cl := auth.Claims{}
		cl.Id = id
		cl.IsAdmin = true
		accessToken, _ := jwt.NewWithClaims(jwt.SigningMethodRS256, cl).SignedString(key)
		ctx.JSON(infra.ResponseOK(accessToken))
	})

	party.Any("/{router:path}", func(context iris.Context) {
		URL, err := url.Parse(context.FullRequestURI())
		if err != nil {
			context.JSON(infra.ResponseInterErr(err, nil))
			return
		}
		newUrl := *context.Request().URL
		for U, P := range table.RouterUrl {
			if U.Scheme == URL.Scheme {
				if strings.HasPrefix(URL.Path, U.Path) {
					newUrl.Scheme = P.Scheme
					newUrl.Host = P.Host
					newUrl.Path = strings.Replace(URL.Path, U.Path, P.Path, 1)
				}
			}
		}

		bodybts, err := context.GetBody()
		if err != nil {
			context.JSON(infra.ResponseInterErr(err, nil))
			return
		}

		//fmt.Printf("method %v %v\n", context.Method(), newUrl.String())

		req, err := http.NewRequest(context.Method(), newUrl.String(), bytes.NewBuffer(bodybts))
		if err != nil {
			context.JSON(infra.ResponseInterErr(err, nil))
			return
		}
		req.Header = context.Request().Header

		cli := http.Client{
			Timeout: time.Duration(time.Minute),
		}
		resp, err := cli.Do(req)
		if err != nil {
			context.JSON(infra.ResponseInterErr(err, nil))
			return
		}

		respBody, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			context.JSON(infra.ResponseInterErr(err, nil))
			return
		}
		context.Write(respBody)
	})
	App.Run(iris.Addr(":8080"))
	<-ctx.Done()
}
